
var Nrg_Ut_App = new Class({
	
	$utTopConsole: null,
	
	$utConsole: null,
	
	$testsCounter: null,
	
	$assertsCounter: null,
	
	$errorsCounter: null,
	
	$summaryEl: null,
	
	$errContainer: null,
	
	_current_suite: '',
	
	_current_tc: '',
	
	_current_fn: '',
	
	_current_assert: 1,
	
	_errors_count: -1,
	
	_tests_count: -1,
	
	_asserts_count: -1,
	
	_test_suites: [],
	
	_trace_objects: {},
	
	_trace_objects_iterator: 0,
	
	//--------------------------------------------------------------------------
	//--------------------------------------------------------------------------
	
	initialize: function($utTopConsole, $utConsole)
	{
		if ( ! instanceOf($utTopConsole, Element) ) {
			throw 'The first parameter must be an instance of Element';
		}
		if ( ! instanceOf($utTopConsole, Element) ) {
			throw 'The second parameter must be an instance of Element';
		}
		this.$utConsole = $utConsole;
		this.$utTopConsole = $utTopConsole;
		
		this.$testsCounter = new Element('div', {
			'class': 'ut_tests_counter'
		});
		this.$assertsCounter = new Element('div', {
			'class': 'ut_asserts_counter'
		});
		
		this.$summaryEl = new Element('div');
		
		this.$errContainer = new Element('ol', {
			'class': 'ut_error_container'
		});
		this.$errorsCounter = new Element('div', {
			'class': 'ut__errors_counter'
		});
		
		this.$testsCounter.inject(this.$utTopConsole);
		this.$assertsCounter.inject(this.$utTopConsole);
		this.$errorsCounter.inject(this.$utTopConsole);
		this.$summaryEl.inject(this.$utTopConsole);
		this.$errContainer.inject(this.$utTopConsole);
		
		this.incrementTestsCount();
		this.incrementAssertsCount();
		this.incrementErrorsCount();
	},
	
	//--------------------------------------------------------------------------
	
	addSuite: function(TestSuite)
	{
		if ( ! instanceOf(TestSuite, Nrg_Ut_Suite) ) {
			throw 'The test case must be an instance of Nrg_Ut_Suite';
		}
		TestSuite.setParentApp(this);
		this._test_suites[this._test_suites.length] = TestSuite;
	},
	
	//--------------------------------------------------------------------------
	
	incrementTestsCount: function()
	{
		this._tests_count ++;
		this.$testsCounter.set('html', 'Tests: ' + this._tests_count);
	},
	
	//--------------------------------------------------------------------------
	
	incrementAssertsCount: function()
	{
		this._asserts_count ++;
		this.$assertsCounter.set('html', 'Assertions: ' + this._asserts_count);
	},
	
	//--------------------------------------------------------------------------
	
	incrementErrorsCount: function()
	{
		this._errors_count ++;
		this.$errorsCounter.set('html', 'Errors: ' + this._errors_count);
	},
	
	//--------------------------------------------------------------------------
	
	logSuite: function(TestSuite)
	{
		this._current_suite = TestSuite.getName();
		var $suiteSpan = new Element('span', {
			html: '<br /><br />' + TestSuite.getName(), 'class': 'utsuite'
		});
		$suiteSpan.inject(this.$utConsole);
	},
	
	//--------------------------------------------------------------------------
	
	logTc: function(name)
	{
		this._current_tc = name;
		this.incrementTestsCount();
		var $el = new Element('span', {
			html: '<br />&nbsp;&nbsp;' + name, 'class': 'utsuite'
		});
		$el.inject(this.$utConsole);
	},
	
	//--------------------------------------------------------------------------
	
	logFn: function(name)
	{
		this._current_fn = name;
		this._current_assert = 1;
		var $el = new Element('span', {
			html: '<br />&nbsp;&nbsp;&nbsp;&nbsp;' + name + ': ',
			'class': 'utfn'
		});
		$el.inject(this.$utConsole);
	},
	
	//--------------------------------------------------------------------------
	
	logAssert: function(result, sysmsg, msg)
	{
		var val = '.';
		var cls = 'utassert_ok';
		
		this.incrementAssertsCount();
		
		if ( ! result ) {
			val = '!';
			cls = 'utassert_fail';
			
			$errEl = this._buildErrDetailsEl(sysmsg, msg);
			$errEl.inject(this.$errContainer);
			
			this.incrementErrorsCount();
		}
		var $fnSpan = new Element('span', {
			html: val, 'class': 'utassert' + cls
		});
		$fnSpan.inject(this.$utConsole);
		++this._current_assert;
	},
	
	//--------------------------------------------------------------------------
	
	run: function()
	{
		new Element('hr').inject(this.$utConsole);
		
		this._test_suites.each(function(Ts){
			this.logSuite( Ts );
			Ts.run();
		}.bind(this));
		
		this._buildSummary();
	},
	
	//--------------------------------------------------------------------------
	
	getCodeFragment: function(trace)
	{
		var Cf = new Nrg_Debug_CodeFragment(trace.href);
		Cf.request(trace.line, function($resEl){
			
		});
	},

	//--------------------------------------------------------------------------
	
	_buildSummary: function()
	{
		var $ResEl = this.$summaryEl;
		var $ResStatus = new Element('strong');
		
		$ResEl.set('text', 'Test result: ');
		
		if ( this._errors_count > 0 ) {
			$ResStatus.set('text', 'FAILED');
			$ResStatus.setStyle('color', 'red');
		}
		else {
			$ResStatus.set('text', 'PASSED');
			$ResStatus.setStyle('color', 'green');
		}
		
		$ResStatus.inject($ResEl);
		return $ResEl;
	},
	
	//--------------------------------------------------------------------------
	
	_buildErrDetailsEl: function(sysmsg, msg)
	{
		var $errEl = new Element('li');
		var $errTbl = new Element('table', {'class': 'ut_err_it_tbl'});
		
		$errTbl.inject($errEl);
		
		var err_rows = [];
		
		err_rows[err_rows.length] = {
			row_class: 'red',
			value_class: 'strong',
			key: 'Assertion error in',
			value: this._current_suite
				+ '.' + this._current_tc
				+ '.' + this._current_fn
				+ '.assert[' + this._current_assert + ']'
		};
		
		err_rows[err_rows.length] = {
			value_class: 'message',
			key: 'System message:',
			value: sysmsg
		};
		
		if ( msg ) {
			err_rows[err_rows.length] = {
				value_class: 'message',
				key: 'User message:',
				value: msg
			};
		}
		
		err_rows.append(this._buildTraceRows());
		
		err_rows.each(function(row){
			var $row = new Element('tr');
			var $key = new Element('td');
			var $val = new Element('td');
			
			$key.inject($row);
			$val.inject($row);
			
			$key.set('html', row.key);
			if ( instanceOf(row.value, Element) ) {
				row.value.inject($val);
			}
			else {
				$val.set('html', row.value);
			}
			
			if ( row.row_class ) {
				$row.addClass('ut_trace_row_' + row.row_class);
			}
			if ( row.value_class ) {
				$val.addClass('ut_trace_cell_' + row.value_class);
			}
			
			$row.inject($errTbl);
		});
		
		return $errEl;
		
	}.protect(),
	
	//--------------------------------------------------------------------------
	
	_buildTraceRows: function()
	{
		var rows = [];
		var Trace = new Nrg_Debug_Trace();
		var tr_it = 0;
		
		Trace.getTrace().each(function(traceEl){
			tr_it ++;
			rows.append(this._buildTraceRowElements(traceEl, tr_it));
		}.bind(this));
		
		return rows;
		
	}.protect(),
	
	//--------------------------------------------------------------------------
	
	_buildTraceRowElements: function(traceEl, tr_it)
	{
		var rows = [];
		if ( [5,6].contains(tr_it) ) {
			rows[rows.length] = {
				value_class: 'strong',
				key: '',
				value: traceEl.trace
			};
			if ( 5 == tr_it ) {
				rows[rows.length - 1].key = 'Trace:';
			}
			else if ( 6 == tr_it ) {
				rows[rows.length] = {
					value_class: 'strong',
					key: '',
					value: this._buildTraceMoreLinkElement(traceEl)
				};
			}
		}
		//c.log(rows);
		return rows;
	}.protect(),
	
	//--------------------------------------------------------------------------
	
	_buildTraceMoreLinkElement: function(traceEl)
	{
		var id = 'trace_more_' + this._trace_objects_iterator;
		this._trace_objects_iterator ++;
		
		var $container = new Element('div', {
			'class': 'ut_trace_show_code_container'
		});
		var $link = new Element('a', {html: 'Show code &gt;'});
		$link.set('href', traceEl.href);
		$link.set('id', id);
		this._trace_objects[id] = traceEl;
		
		$link.addEvent('click', function(Evt){
			Evt.stop();
			trace = this._trace_objects[Evt.target.get('id')];
			
			var $next = Evt.target.getNext();
			
			if ( $next ) {
				if ( $next.isDisplayed()  ) {
					Evt.target.set('html', 'Show code &gt;');
					$next.hide();
				}
				else {
					Evt.target.set('html', 'Hide code &lt;');
					$next.show();
				}
			}
			else {
				Evt.target.set('html', 'Hide code &lt;');
				var Cf = new Nrg_Debug_CodeFragment(trace.href);
				Cf.request(trace.line, function($resEl){
					$resEl.inject(Evt.target.getParent());
				});
			}
			
		}.bind(this));
		
		$link.inject($container);
		//return $container.get('html');
		return $container;
		
	}.protect()
	
	//--------------------------------------------------------------------------
	
});